/*
 * jxSwing Project (2014)
 * 
 * by Denilson Edinaldo Pinto
 * 
 * LGPL
 * Copyright (C) 2014
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package org.jxSwing.component.tree;

import java.io.File;
import java.io.FileFilter;
import java.util.Iterator;
import java.util.Vector;

import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;

import org.jxSwing.i18n.I18N;
import org.jxUtils.collection.CollectionUtil;

/**
 * @author Denilson Edinaldo Pinto
 * @porject jxSwing
 * @package org.jxSwing.component.tree
 * @date 14/06/2014 - 09:42:34
 */
public class FileTreeModel implements TreeModel {
	
	private static final File SYSTEM_ROOT = new File(I18N.system().computer());
	
	private final Vector<TreeModelListener> treeModelListener = new Vector<>();
	
	private File rootFile = null;
	
	private boolean showHideFiles = false;
	
	private boolean showOnlyDirectories = false;
	
	/**
	 * 
	 */
	public FileTreeModel() {
	
		this(SYSTEM_ROOT, false);
	}
	
	/**
	 * @param rootFile
	 * @param showHideFiles
	 */
	public FileTreeModel(final File rootFile, final boolean showHideFiles) {
	
		this(SYSTEM_ROOT, false, false);
	}
	
	/**
	 * @param rootFile
	 * @param showHideFiles
	 * @param showOnlyDirectories
	 */
	public FileTreeModel(final File rootFile, final boolean showHideFiles, final boolean showOnlyDirectories) {
	
		this.rootFile = rootFile == null ? SYSTEM_ROOT : rootFile.isFile() ? rootFile.getParentFile() : rootFile;
		this.showHideFiles = showHideFiles;
		this.showOnlyDirectories = showOnlyDirectories;
	}
	
	/**
	 * @param parentPath
	 * @param index
	 * @param children
	 */
	private void fireChanged(final TreePath parentPath, final int[] index, final Object[] children) {
	
		final TreeModelEvent event = new TreeModelEvent(this, parentPath, index, children);
		
		final Iterator<TreeModelListener> iterator = treeModelListener.iterator();
		
		//
		while(iterator.hasNext()) {
			
			final TreeModelListener listener = iterator.next();
			
			listener.treeNodesChanged(event);
		}
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.tree.TreeModel#addTreeModelListener(javax.swing.event.TreeModelListener)
	 */
	@Override
	public void addTreeModelListener(final TreeModelListener listener) {
	
		treeModelListener.add(listener);
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.tree.TreeModel#getChild(java.lang.Object, int)
	 */
	@Override
	public FileNode getChild(final Object parent, final int index) {
	
		File directory = (File)parent;
		final File[] children = directory.equals(SYSTEM_ROOT) ? File.listRoots() : directory.listFiles(new TreeFileFilter());
		
		CollectionUtil.sort(children);
		
		directory = directory.equals(SYSTEM_ROOT) ? children[index] : directory;
		
		return new FileNode(directory, children[index]);
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.tree.TreeModel#getChildCount(java.lang.Object)
	 */
	@Override
	public int getChildCount(final Object parent) {
	
		final File file = (File)parent;
		
		//
		if(file.equals(SYSTEM_ROOT) || file.isDirectory()) {
			
			final File[] files = file.equals(SYSTEM_ROOT) ? File.listRoots() : file.listFiles(new TreeFileFilter());
			
			CollectionUtil.sort(files);
			
			//
			if(files != null) {
				
				return files.length;
			}
		}
		
		return 0;
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.tree.TreeModel#getIndexOfChild(java.lang.Object, java.lang.Object)
	 */
	@Override
	public int getIndexOfChild(final Object parent, final Object child) {
	
		final File directory = (File)parent;
		final File file = (File)child;
		final File[] children = directory.equals(SYSTEM_ROOT) ? File.listRoots() : directory.listFiles(new TreeFileFilter());
		
		CollectionUtil.sort(children);
		
		//
		for(int i = 0; i < children.length; i++) {
			
			//
			if(file.getName().equals(children[i].getName())) {
				
				return i;
			}
		}
		
		return -1;
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.tree.TreeModel#getRoot()
	 */
	@Override
	public File getRoot() {
	
		return rootFile;
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.tree.TreeModel#isLeaf(java.lang.Object)
	 */
	@Override
	public boolean isLeaf(final Object node) {
	
		return ((File)node).isFile();
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.tree.TreeModel#removeTreeModelListener(javax.swing.event.TreeModelListener)
	 */
	@Override
	public void removeTreeModelListener(final TreeModelListener listener) {
	
		treeModelListener.remove(listener);
	}
	
	/**
	 * @return the showHideFiles
	 */
	public boolean showHideFiles() {
	
		return showHideFiles;
	}
	
	/**
	 * @return the showHideFiles
	 */
	public boolean showOnlyDirectories() {
	
		return showOnlyDirectories;
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.tree.TreeModel#valueForPathChanged(javax.swing.tree.TreePath, java.lang.Object)
	 */
	@Override
	public void valueForPathChanged(final TreePath path, final Object value) {
	
		final File oldFile = (File)path.getLastPathComponent();
		
		final String parentPath = oldFile.getParent();
		final String newFileName = (String)value;
		
		final File targetFile = new File(parentPath, newFileName);
		
		oldFile.renameTo(targetFile);
		
		final File parent = new File(parentPath);
		
		//
		fireChanged(path.getParentPath(), new int[]{getIndexOfChild(parent, targetFile)}, new File[]{targetFile});
	}
	
	/**
	 * @author Denilson Edinaldo Pinto
	 * @porject jxSwing
	 * @package org.jxSwing.component.tree
	 * @date 14/06/2014 - 09:47:32
	 */
	private class FileNode extends File {
		
		private static final long serialVersionUID = -8539835373739167902L;
		
		/**
		 * @param child
		 */
		public FileNode(final File child) {
		
			super(child.getAbsolutePath());
		}
		
		/**
		 * @param parent
		 * @param child
		 */
		public FileNode(final File parent, final File child) {
		
			super(parent, child.getName());
		}
		
		/*
		 * (non-Javadoc)
		 * 
		 * @see java.io.File#toString()
		 */
		@Override
		public String toString() {
		
			return getName().trim().isEmpty() ? getAbsolutePath() : getName();
		}
	}
	
	/**
	 * @author Denilson Edinaldo Pinto
	 * @porject jxSwing
	 * @package org.jxSwing.component.tree
	 * @date 14/06/2014 - 10:30:41
	 */
	private class TreeFileFilter implements FileFilter {
		
		/*
		 * (non-Javadoc)
		 * 
		 * @see java.io.FileFilter#accept(java.io.File)
		 */
		@Override
		public boolean accept(final File file) {
		
			return file.isHidden() == showHideFiles() && (showOnlyDirectories() ? file.isDirectory() : true);
		}
	}
}
